/**
 *	\file AKTTTStateMaintainer.m
 *  
 *  \section desc_sec Description
 *  Description
 *  
 *  \if copyright
 *  	Copyright 2008 Ben Chen. All rights reserved.
 *  \endif
 *  
 *  \section hist_sec Change History:
 *  	-	1/16/08		BC		Created this file
 *  
 *  <hr>
 *  \addtogroup AnimalKingdom
 *  @{
 */

#import "AKTTT.h"
#import "AKTTTStateMaintainer.h"

#define KeyState			@"state"
#define KeyNextPlayerSide	@"nextPlayerSide"

@interface AKTTTStateMaintainer ()
@property (readwrite, copy) NSArray *eliminatedPieces, *createdPieces, *movedPieces, *state, *winners;
@property (readwrite, assign) AKSideIndex nextPlayerSide;
@end

@implementation AKTTTStateMaintainer

@synthesize eliminatedPieces, createdPieces, movedPieces, state, winners, nextPlayerSide;

- (id) init
{
	self = [super init];
	if (self != nil) 
	{
		[self backToInitialState];
	}
	return self;
}

- (void) dealloc
{
	[super dealloc];
}

#pragma mark Helper Function


inline BOOL isSameInLine(unsigned start, unsigned end, unsigned inter, AKPiece pieces[]) {
	BOOL retVal = NO;
	
	if (pieces[start].side != NSNotFound) {
		unsigned i;
		for (i = start+inter; i <= end; i+= inter) {
			if (pieces[i].side != pieces[start].side)
				break;
		}
		
		if (i > end)
			retVal = YES;
	}
	
	return retVal;
}

- (AKSideIndex)findWinner
{
	AKSideIndex retVal = NSNotFound;
	
	// 0 1 2
	// 3 4 5
	// 6 7 8
	
	// Three in a row
	if (isSameInLine(0, 2, 1, pieces))
		retVal = pieces[0].side;
	else if (isSameInLine(3, 5, 1, pieces))
		retVal = pieces[3].side;
	else if (isSameInLine(6, 8, 1, pieces))
		retVal = pieces[6].side;
	// Three in a column
	else if (isSameInLine(0, 6, 3, pieces))
		retVal = pieces[0].side;
	else if (isSameInLine(1, 7, 3, pieces))
		retVal = pieces[1].side;
	else if (isSameInLine(2, 8, 3, pieces))
		retVal = pieces[2].side;
	// Three in a diagnal
	else if (isSameInLine(0, 8, 4, pieces))
		retVal = pieces[0].side;
	else if (isSameInLine(2, 6, 2, pieces))
		retVal = pieces[2].side;
	
	return retVal;
}		

- (BOOL)boardIsFull {
	BOOL retVal = NO;
	
	int i;
	for (i = 0; i < AKTTTNumOfCells; ++i) {
		if (pieces[i].piece == NSNotFound)
			break;
	}
	
	if (i == AKTTTNumOfCells)
		retVal = YES;
	
	return retVal;
}

- (NSMutableArray*)calcState
{
	NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:AKTTTNumOfCells];
	
	int i;
	for (i = 0; i < AKTTTNumOfCells; ++i) 
	{
		NSData *cellData = [NSData dataWithBytes:&pieces[i] length:sizeof(AKPiece)];
		[tempArray addObject:cellData];
	}
	
	return tempArray;
}	

/**
 *  Go back to initial state.
 */
- (void)backToInitialState
{
	int i;
	for (i = 0; i < AKTTTNumOfCells; ++i) 
	{		
		pieces[i].cell	= i;
		pieces[i].piece	= NSNotFound;
		pieces[i].side	= NSNotFound;
	}
	
	self.createdPieces	= [self calcState];
	self.nextPlayerSide	= AKTTTBlack;	// black goes first
	self.winners		= nil;
}

/**
 *  Perform a move.
 *  \param[in] newMove A new move.
 *  \return YES if the move has been made, otherwise NO meaning the move is not permitted.
 */
- (BOOL)performMove:(AKMove)newMove
{
	BOOL retVal;
	
	if (winners != nil || newMove.dstCell > AKTTTNumOfCells || newMove.side != nextPlayerSide || newMove.piece > 0)
		retVal = NO;
	else
	{
		AKPiece *pieceInfo = &pieces[newMove.dstCell];
		
		if (pieceInfo->piece != NSNotFound)
			// The cell must be empty
			retVal = NO;
		else {
			pieceInfo->cell  = newMove.dstCell;
			pieceInfo->piece = newMove.piece;
			pieceInfo->side  = newMove.side;
			
			// Change state
			[self setState:[self calcState]];
						
			// Find winner
			AKSideIndex newWinner = [self findWinner];
			if (newWinner != NSNotFound) {
				self.winners = [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:newWinner]];
				self.nextPlayerSide = NSNotFound;
			}
			else if ([self boardIsFull]) {
				self.winners = [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:0], [NSNumber numberWithUnsignedInt:1], nil];
				self.nextPlayerSide = NSNotFound;
			}				
			else
				// Change side
				[self setNextPlayerSide:((nextPlayerSide == AKTTTBlack) ? AKTTTWhite : AKTTTBlack)];
			
			retVal = YES;
		}
	}
			
	return retVal;
}

/**
 *  Get cells that the given existing piece can move to.
 *  \param[in] cell The info about the given piece.
 *  \return Array of AKCellIndex wrapped in NSNumber.
 */
- (NSArray*)reachableCellsForPiece:(AKPiece)piece
{
	// Since we can't move pieces in Tic Tac Toe
	return nil;
}

/**
 *  Get the type of pieces of the current side that can be created and placed at the given cell.
 *  e.g. In the game of Tic Tac Toe, every new move is made by creating a new piece at an empty cell.
 *  \param[in] cell The index of the given cell.
 *  \return Array of AKPieceIndex wrapped in NSNumber;
 */
- (NSArray*)newPiecesAtCell:(AKCellIndex)cell {
	NSArray *retVal = nil;
	
	// Validate the cell index
	if (cell < AKTTTNumOfCells) {
		// Check if the cell is empty
		AKPiece *cellInfo = (AKPiece*)[[state objectAtIndex:cell] bytes];
		if (cellInfo->piece == NSNotFound) {
			retVal = [NSArray arrayWithObject:[NSNumber numberWithInt:0]];
		}
	}

	return retVal;
}

- (AKPiece)moveablePieceAtCell:(AKCellIndex)cell
{
	AKPiece retVal = AKZeroPiece;
	
	if (cell >= 0 && cell <= 9 && pieces[cell].piece != NSNotFound)
		retVal = pieces[cell];
	
	return retVal;
}

#pragma mark KVC methods

/**
 *  Return the current state in the form of cells that have pieces on them.
 *  \return Array of AKCell wrapped in NSData.
 */
- (NSArray*)state
{
	return state;
}

/**
 *  Return the side of the next player.
 *  \return NSNotFound if no player (e.g. someone wins already), otherwise the index of the next player.
 */
- (AKSideIndex)nextPlayerSide
{
	return nextPlayerSide;
}

/**
 *  Get the moveable pieces of the current side.
 *  \return Array of AKCell wrapped in NSData.
 */
- (NSArray*)moveablePieces
{
	return nil;
}

@end

/** @} */
